%%% -------------------------------------------------------------------
%%% Author  : vjache
%%% Description : Cluster Membership Service
%%%
%%% Created : 19.02.2009
%%% -------------------------------------------------------------------
-module(svc_cms).

-behaviour(gen_server). 
%% --------------------------------------------------------------------
%% Include files
%% --------------------------------------------------------------------
-include("../include/util.hrl").

-define(SERVER,?MODULE).

%% --------------------------------------------------------------------
%% External exports
-export([start_link/2,get_randezvouse/0]).

%% gen_server callbacks
-export([
         init/1, 
         handle_call/3, 
         handle_cast/2, 
         handle_info/2, 
         terminate/2, 
         code_change/3,
		 shutdown_all/0,
		 shutdown_node/0]).

-record(state, {rand}).

-define(CHECKERR(Result,ErrCode),checkerr(Result,ErrCode)).

%% ====================================================================
%% External functions
%% ====================================================================
get_randezvouse() ->
	gen_server:call(?SERVER, get_randezvouse).

shutdown_all() ->
	ok=gen_server:cast(?SERVER, shutdown_all).
shutdown_node() ->
	ok=gen_server:cast(?SERVER, shutdown_node).
	

%% ====================================================================
%% Server functions
%% ====================================================================

%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link(RandNode,Options) ->
	io:format("svc_cms:start_link~n"),
    gen_server:start_link({local,?SERVER}, ?MODULE, _InitArgs=[RandNode], Options).

%% --------------------------------------------------------------------
%% Function: init/1
%% Description: Initiates the server
%% Returns: {ok, State}          |
%%          {ok, State, Timeout} |
%%          ignore               |
%%          {stop, Reason}
%% --------------------------------------------------------------------
init([RandNode]) ->
	io:format("svc_cms:init~n"),
    io:format("Randezvous node ~p.~n",[RandNode]),
	try init_mnesia(RandNode) of
		_ ->
			ok
	catch
		_:Err ->
			error_logger:error_report(
			  [
			   {reason,Err},
			   {stack_trace,erlang:get_stacktrace()}
			  ]),
			throw(Err)
	end,
	io:format("svc_cms:init 1~n"),
    {ok, #state{rand=RandNode}}.

%% --------------------------------------------------------------------
%% Function: handle_call/3
%% Description: Handling call messages
%% Returns: {reply, Reply, State}          |
%%          {reply, Reply, State, Timeout} |
%%          {noreply, State}               |
%%          {noreply, State, Timeout}      |
%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_call(get_randezvouse, _From, State) ->
	Reply = State#state.rand,
    {reply, Reply, State};
handle_call(ping, _From, State) ->
	Reply = ok,
    {reply, Reply, State};
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%% --------------------------------------------------------------------
%% Function: handle_cast/2
%% Description: Handling cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_cast(shutdown_all, State) ->
	?LOG_INFO("shutdown_all.",[]),
	Nodes=nodes(),
	[true=monitor_node(Node, true)||Node<-Nodes],
	[ok=gen_server:cast({?SERVER,Node}, shutdown_node)||Node<-Nodes],
	wait_shutdown(Nodes),
	?LOG_INFO("Shutdown self.",[]),
	ok=gen_server:cast(?SERVER, shutdown_node),
	{noreply, State};
handle_cast(shutdown_node, State) ->
	?LOG_INFO("Stoping mnesia...",[]),
	ok=terminate(shutdown_node,State),
	?LOG_INFO("Halting...",[]),
	halt(),
	{noreply, State};
handle_cast(_Msg, State) ->
    {noreply, State}.

wait_shutdown([]) ->
	ok;
wait_shutdown(Nodes) ->
	receive
		{nodedown, Node} ->
			Nodes1=lists:delete(Node, Nodes),
			?LOG_INFO("Node ~p down",[Node]),
			wait_shutdown(Nodes1)
	end.
%% --------------------------------------------------------------------
%% Function: handle_info/2
%% Description: Handling all non call/cast messages
%% Returns: {noreply, State}          |
%%          {noreply, State, Timeout} |
%%          {stop, Reason, State}            (terminate/2 is called)
%% --------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%% --------------------------------------------------------------------
%% Function: terminate/2
%% Description: Shutdown the server
%% Returns: any (ignored by gen_server)
%% --------------------------------------------------------------------
terminate(_Reason, _State) ->
	stopped=mnesia:stop(),
    ok.

%% --------------------------------------------------------------------
%% Func: code_change/3
%% Purpose: Convert process state when code is changed
%% Returns: {ok, NewState}
%% --------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------

init_mnesia(RandNode)->
	case node() of
		nonode@nohost -> exit(node_is_not_alive);
		RandNode ->
			case mnesia:create_schema([RandNode]) of
				ok -> ok;
				{error,{_,{already_exists,_}}}->ok
			end,
			?CHECKERR(mnesia:start(),mnesia_start_failure);
		Node ->
			ok=wait_randezvouse(RandNode),
			case rpc:call(
				   RandNode, mnesia, 
				   add_table_copy, 
				   [schema, Node, ram_copies]) of
				{aborted,{already_exists,schema,Node}} ->
					?CHECKERR(mnesia:start(),mnesia_start_failure);
				{atomic,ok} ->
					?CHECKERR(mnesia:start(),mnesia_start_failure),
					?CHECKERR(mnesia:change_config(extra_db_nodes, [RandNode]),change_config_failure),
					?CHECKERR(mnesia:change_table_copy_type(schema, node(), disc_copies),change_config_failure);
				Err ->
					?CHECKERR(Err,schema_node_add_failure)
			end
	end.

checkerr({badrpc, _}=Err,ErrCode) ->
	throw({ErrCode,Err});
checkerr({aborted, _}=Err,ErrCode) ->
	throw({ErrCode,Err});
checkerr({error, Err},ErrCode) ->
	throw({ErrCode,Err});
checkerr(Result,_ErrCode) ->
	Result.


wait_randezvouse(RandNode)->
	?LOG_INFO("Wait randezvous ~p up...",[RandNode]),
	case net_adm:ping(RandNode) of
		pang ->
			ok=timer:sleep(5*1000),
			wait_randezvouse(RandNode);
		pong ->
			wait_server(RandNode)
	end,
	ok.

wait_server(RandNode) ->
	?LOG_INFO("Wait randezvous service on ~p ...",[RandNode]),
	case (catch gen_server:call({?SERVER,RandNode}, ping, 5*1000)) of
%% 		{'EXIT',{timeout,{gen_server,call,_}}}->
		ok -> ok;
		_ ->
			wait_server(RandNode)
	end.



